#include "url.hpp"
#include "ary_str.hpp"
#include <regex>

URL :: 
Param ::Param()
{

}

URL :: 
Param ::Param( const std::string& query )
{
	bool rst = parse( query );
	if( rst == false ){
		throw URL :: ERR_PARSE_QUERY;
	}
}

URL :: 
Param :: ~Param()
{

}

bool URL :: 
Param :: parse( const std::string& query )
{
	bool ret = false;
	if( query.empty() ) return false;

	std::string str = query;
	std::regex reg( "(\\w|%[0-9A-Fa-f]{2,4}|-|_)+=(\\w|%[0-9A-Fa-f]{2,4}|-|_)+" );
	
	std::smatch  sm;
	int count = 0;

	while( std::regex_search( str , sm , reg ) == true ){
		count ++;

		std::string str1 = sm[ 0 ], key , param;
		key = std::move( str1.substr( 0 , str1.find( "=" ) ) );
		param = std::move( str1.substr( str1.find( "=" ) + 1 ) );

		m_dict.insert( std::pair< std::string , std::string>( key , param ) );

		str = std::move( std::regex_replace( str , reg , "$1" ) );
	}
	ret = ( count > 0 );
	return ret;
}

const std::string URL :: 
Param ::operator[ ]( const std::string& name )
{
	std::string ret;
	if( name.empty() ) throw URL :: ERR_EMPTY_PARAM_KEY;
	if( m_dict.find( name ) == m_dict.end() ) throw URL :: ERR_KEY_NOT_EXIST;

	ret = m_dict[ name ];

	return ret;
}

size_t URL :: 
Param ::size()
{
    	return m_dict.size();
}
////////////////////////////////////////////////////////////////////////////////////////////
bool URL :: parse_protocol( const std::string& url )
{
	bool ret = false;
	std::regex reg( "^\\w+(?=://)" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_protocol = sm[ 0 ];
		ret = true;
	}

	return ret;
}

bool URL :: parse_user( const std::string& url )
{
	bool ret = false;
	std::regex reg( "(?:\\w+://)(\\w|_)+(?=:)" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_user = sm[ 0 ];
		ret = true;
	}

	return ret;
}

bool URL :: parse_password( const std::string& url )
{
	bool ret = false;
	std::regex reg( "(?:)(\\w|_)+(?=@)" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_passwd = sm[ 0 ];
		ret = true;
	}

	return ret;
}

bool URL :: parse_host( const std::string& url )
{
	bool ret = false;

	std::regex reg( "(?:(://|@))(\\w|_)+(\\.(\\w|_)+)+" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_host = sm[ 0 ];
		ret = true;
	}

	return ret;
}

bool URL :: parse_port( const std::string& url )
{
	bool ret = false;

	std::regex reg( "(?:)\\d+(?=\\/\\w+)" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_port = sm[ 0 ];
		ret = true;
	}
	
	return ret;
}

bool URL :: parse_query( const std::string& url )
{
	bool ret = false;

	std::regex reg( "(?:\\?)(\\w|%[0-9A-Fa-f]{2,4}|-|_)+=(\\w|%[0-9A-Fa-f]{2,4}|-|_)+(&(\\w|%[0-9A-Fa-f]{2,4}|-|_)+=(\\w|%[0-9A-Fa-f]{2,4}|-|_)+)*");   
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_query = sm[ 0 ];

		ret = m_param( m_query );
		if( !ret ){
		m_param.clear();
		m_query.clear();
		}
	}else{
		m_param.clear();
		m_query.clear();
	}
	
	return ret;
}

bool URL :: parse_path( const std::string& url )
{
	bool ret = false;
	std::regex reg( "(/(\\w|_|\\.)*)+(?=\\?)" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_path = sm[ 0 ];
		ret = true;

	}else m_hash.clear();
	return ret;
}

bool URL :: parse_hash( const std::string& url )
{
	bool ret = false;
	std::regex reg( "(?:#)(\\w|_)+" );
	std::smatch sm;

	if( std::regex_search( url , sm , reg ) == true ){
		m_hash = sm[ 0 ];
		ret = true;

	}else m_hash.clear();
	return ret;
}

URL :: URL(){}

URL :: URL( const std::string& url )
{
	if( url.empty() ) throw ERR_EMPTY_URL;

	parse_protocol( url );
	parse_user( url );
	parse_password( url );
	parse_host( url );
	parse_port( url );
	parse_path( url );
	parse_query( url );
	parse_hash( url );
}

URL ::  ~URL(){}

const std::string URL :: Protocol() const
{
    	return m_protocol;
}

const std::string URL :: AuthUser() const
{
    	return m_user;
}

const std::string URL :: AuthPswd() const
{
    	return m_passwd;
}

const std::string URL :: HostName() const
{
    	return m_host;
}

const std::string URL :: HostPort() const
{
    	return m_port;
}

const std::string URL :: Path() const
{
    	return m_path;
}

const std::string URL :: Query() const
{
    return m_query;
}

const std::string URL :: Parameters( const std::string& key )
{
	if( key.empty() ) throw ERR_EMPTY_PARAM_KEY;
	if( m_param.find( key ) == m_param.end() ) throw ERR_KEY_NOT_EXIST;

	return m_param[ key ]; 
}

void URL :: Params( Param& param )
{
    	param = m_param;
}

const std::string URL ::  Hash() const
{
    return m_hash;
}

const std::string URL :: Encode( const std::string& from )
{
	std::string ret("");

	return ret;
}

const std::string URL :: Decode( const std::string& from )
{
	std::string ret("");

	return ret;
}

bool URL :: Parse( const std::string& url )
{   
	if( url.empty() ) return false;

	bool ret = parse_protocol( url );
	parse_user( url );
	parse_password( url );
	bool ret1 = parse_host( url );
	parse_port( url );
	bool ret2 = parse_path( url );
	parse_query( url );
	parse_hash( url );

	return ret && ret1 && ret2;
}